import numpy as np
import pandas as pd
from requests import Response

from tools.myredis import r
from sklearn.metrics.pairwise import cosine_similarity


# 读取 CSV 文件并将其存储为 pandas.DataFrame。
# 创建用户和商品的唯一列表，同时创建用户和商品到其索引的映射。
# 创建一个零矩阵 data 用于存储用户 - 商品矩阵，将用户对商品的交互标记为 1（可根据实际情况修改交互的标记）。
# 遍历 DataFrame 的每一行，根据映射将用户和商品的交互标记添加到 data 矩阵中

def load_data(file_path):
    """
    从 CSV 文件中加载数据，并将其转换为用户-商品矩阵。
    :param file_path: CSV 文件的路径
    :return: 用户-商品矩阵，用户列表，商品列表
    """
    df = pd.read_csv(file_path)
    users = df['userid'].unique()
    goods = df['goodsid'].unique()
    user_index_map = {user: index for index, user in enumerate(users)}
    good_index_map = {good: index for index, good in enumerate(goods)}
    # np.zeros() 是 numpy 库中的一个函数，用于创建一个全零数组。
    # (len(users), len(goods)) 是作为 np.zeros() 函数的参数，它是一个元组，用于指定生成数组的形状。
    # len(users) 表示用户的数量，它将作为生成数组的行数。
    # len(goods) 表示商品的数量，它将作为生成数组的列数。

    data = np.zeros((len(users), len(goods)))
    # iterrows() 是 pandas.DataFrame 的一个方法，用于迭代 DataFrame 中的每一行。
    # _ 是一个占位符，因为 iterrows() 会返回一个元组 (index, row)，其中 index 是行的索引，        #row 是包含该行数据的 Series 对象。这里我们不需要行的索引，所以使用 _ 来忽略它。
    # row 是一个 pandas.Series 对象，包含了当前迭代行的所有数据。
    for _, row in df.iterrows():
        user_index = user_index_map[row['userid']]
        good_index = good_index_map[row['goodsid']]
        # data 是一个 numpy 数组，可能用于存储用户 - 商品的交互信息。
        # user_index 和 good_index 分别是用户和商品的索引，将 data 数组中相应位置的元素设置为 1，表示用户和商品之间存在某种交互（例如，用户购买了商品）。
        data[user_index][good_index] = 1
    return data, users, goods


# 使用 cosine_similarity 函数计算用户之间的余弦相似度矩阵。
# 获取目标用户与其他用户的相似度，并将目标用户自身的相似度设为 0。
# 找到最相似的用户，遍历相似用户的商品评价。
# 对于目标用户未交互且相似用户交互过的商品，将其添加到推荐列表中，直到达到推荐数量 top_n
def user_based_recommendation(data, user_index, top_n=5):
    """
    基于用户的协同过滤推荐。
    :param data: 用户-商品矩阵
    :param user_index: 目标用户的索引
    :param top_n: 推荐商品的数量
    :return: 推荐的商品列表
    """
    # 计算用户之间的余弦相似度
    user_similarity = cosine_similarity(data)
    # 目标用户与其他用户的相似度,从 user_similarity 矩阵中提取出目标用户（由 user_index 指定）与其他用户的相似度向量。
    target_user_similarity = user_similarity[user_index]
    # 排除用户自身的相似度
    target_user_similarity[user_index] = 0
    # 对相似度进行排序，找到最相似的用户,使用 np.argsort 对 target_user_similarity 进行排序，[::-1] 是切片操作，将结果反转，使最相似的用户排在前面。np.argsort 返回排序后元素的索引，而不是元素本身
    similar_users_indices = np.argsort(target_user_similarity)[::-1]
    # 首先，创建一个空列表 recommended_goods 用于存储推荐的商品索引。
    recommended_goods = []
    # 然后，遍历 similar_users_indices，对于每个相似用户的索引 similar_user_index：
    for similar_user_index in similar_users_indices:
        # 获取该相似用户的商品评价 similar_user_ratings。
        similar_user_ratings = data[similar_user_index]
        # 遍历 similar_user_ratings 的每个商品索引 good_index：
        for good_index in range(len(similar_user_ratings)):
            # 如果目标用户没有与该商品交互（data[user_index][good_index] == 0）且相似用户与该商品有交互（similar_user_ratings[good_index] == 1），将该商品添加到 recommended_goods 中。
            # 一旦推荐商品数量达到 top_n，退出循环。
            if data[user_index][good_index] == 0 and similar_user_ratings[good_index] == 1:
                recommended_goods.append(good_index)
                if len(recommended_goods) >= top_n:
                    break
        if len(recommended_goods) >= top_n:
            break
    return recommended_goods


def main():
    # 加载数据
    if r.get():
        return Response
    else:
        data, users, goods = load_data('orders.csv')
        # 假设我们要为第一个用户进行推荐
        user_index = 0
        recommended_goods_indices = user_based_recommendation(data, user_index)
        # 将商品索引转换为商品 ID
        recommended_goods_ids = [goods[index] for index in recommended_goods_indices]
        # 推荐的商品id，根据id查询商品表 in
        # 序列化，存入redis，返回
    print(f"Recommended goods for user {users[user_index]}: {recommended_goods_ids}")


if __name__ == "__main__":
    main()